#import <appkit/appkit.h>

#import "chess_strings.h"
#import "Chess.h"
#import "gnuchess.h"
#import "Board.h"
#import "Square.h"

@implementation Board

#define BLACK_SQUARE_COLOR  0.5
#define WHITE_SQUARE_COLOR  (5.0/6.0)

+ newFrame: (const NXRect *)f
{
  int r, c;
  
  self = [super newFrame: f];
  [self allocateGState];
  backBitmap = [Bitmap newSize: f->size.width / 8.0 
		  : f->size.height / 8.0 type: NX_UNIQUEBITMAP];
  [backBitmap setFlip: YES];
  for( r = 0; r < 8; r++ )
    for( c = 0; c < 8; c++ ){
      square[r][c] = [Square new];
      [square[r][c] setBackground: (r+c)%2 == 0 ? BLACK_SQUARE_COLOR : WHITE_SQUARE_COLOR];
    }
  [self setupPieces];
  return self;
}

- setupPieces
{
  [self layoutBoard: Stboard color:Stcolor];
}

- layoutBoard: (short *)b color: (short *)c
{
  int sq, row, col;
  
  for (sq = 0; sq < 64; sq++){
    row = sq / 8;
    col = sq % 8;
    switch( b[sq] ){
    case pawn:
      [self placePiece: c[sq]==white?WHITE_PAWN:BLACK_PAWN at: row: col];
      break;
    case rook:
      [self placePiece: c[sq]==white?WHITE_ROOK:BLACK_ROOK at: row: col];
      break;
    case knight:
      [self placePiece: c[sq]==white?WHITE_KNIGHT:BLACK_KNIGHT at: row: col];
      break;
    case bishop:
      [self placePiece: c[sq]==white?WHITE_BISHOP:BLACK_BISHOP at: row: col];
      break;
    case king:
      [self placePiece: c[sq]==white?WHITE_KING:BLACK_KING at: row: col];
      break;
    case queen:
      [self placePiece: c[sq]==white?WHITE_QUEEN:BLACK_QUEEN at: row: col];
      break;
    default:
      [self placePiece: 0 at: row: col];
      break;
    }
  }
}

- slidePieceFrom: (int)r1 : (int)c1 to: (int)r2 : (int)c2
{
  NXPoint p, backP, endP;
  NXRect pieceRect;
  int controlGState, i, increments;
  char const *icon;
  NXCoord incX, incY;
  
  
  icon = [square[r1][c1] icon];
  if( !icon )
    return self;
    
  controlGState = [self gState];
  pieceRect.size.width = floor( frame.size.width / 8.0 );
  pieceRect.size.height = floor( frame.size.height / 8.0 );
  backP.x = (((float)c1) * pieceRect.size.width);
  backP.y = (((float)r1) * pieceRect.size.height);
  endP.x = (((float)c2) * pieceRect.size.width);
  endP.y = (((float)r2) * pieceRect.size.height);

  [self lockFocus];
  PSgsave();
    
  /* Draw over the piece we are moving. */
  pieceRect.origin.x = c1 * pieceRect.size.width;
  pieceRect.origin.y = r1 * pieceRect.size.height;
  [square[r1][c1] drawBackground: &pieceRect inView: self];
  
  /* Save background */ 
  [backBitmap lockFocus];
  PSgsave();
  PScomposite( backP.x, backP.y, pieceRect.size.width, pieceRect.size.height,
	       controlGState, 0.0, pieceRect.size.height, NX_COPY);
  PSgrestore();
  [backBitmap unlockFocus];
  
  incX = endP.x - backP.x;
  incY = endP.y - backP.y;
  increments = (int) MAX( ABS(incX), ABS(incY) ) / 7;
  incX = incX / increments;
  incY = incY / increments;
  for( i = 0; i < increments - 1; i++ ){

    /* Restore old background */
    [self lockFocus];
    [backBitmap composite:NX_COPY toPoint: &backP];
    [self unlockFocus];

    /* Save new background */
    backP.x += incX;
    backP.y += incY;
    backP.x = floor( backP.x );
    backP.y = floor( backP.y );
    pieceRect.origin = backP;

    [backBitmap lockFocus];
    // PSgsave();
    PScomposite( backP.x, backP.y,
		 pieceRect.size.width, pieceRect.size.height,
		 controlGState, 0.0, pieceRect.size.height, NX_COPY);
    // PSgrestore();
    [backBitmap unlockFocus];

    /* Draw piece at new location. */
    [square[r1][c1] drawInside: &pieceRect inView: self];

    PSsetgray( NX_BLACK );
    PSsetlinewidth( 2.0 );
    PSclippath();
    PSstroke();

    PSflushgraphics();
    NXPing();
  }
 
  PSgrestore();
  [self unlockFocus];
}


- placePiece: (const char *)p at: (int)r : (int)c
{
  [square[r][c] setIcon: p];
}


- (int)typeAt: (int)r : (int)c
{
  return( [square[r][c] type] );
}
  
- highlightSquareAt: (int)r : (int) c
{
  NXRect cr;
  
  [self lockFocus];
  cr.size.width = frame.size.width / 8;
  cr.size.height = frame.size.height / 8;
  cr.origin.x = c * cr.size.width;
  cr.origin.y = r * cr.size.height;
  
  [square[r][c] highlight: &cr inView: self];

  PSsetgray( NX_BLACK );
  PSsetlinewidth( 2.0 );
  PSclippath();
  PSstroke();
  PSflushgraphics();
  NXPing();
  [self unlockFocus];
}
    
- flashSquareAt: (int)r : (int) c
{
}



- printPSCode: sender
{
  id pi = [NXApp printInfo];
  const NXRect *pr;
  NXCoord lm, rm, tm, bm;
  
  pr = [pi paperRect];
  lm = rm = (pr->size.width - frame.size.width) / 2.0;
  tm = bm = (pr->size.height - frame.size.height) / 2.0;
  [pi setMarginLeft: lm right: rm top: tm bottom: bm];
  
  [self lockFocus];
  NXSizeBitmap( &bounds, &print_size, &print_pwide, &print_phigh,
                &print_bps, &print_ssp, &print_config, &print_mask );
  print_image = (void *)malloc( print_size );
  NXReadBitmap( &bounds, print_pwide, print_phigh, print_bps, print_ssp,
	       print_config, print_mask, print_image, 0, 0, 0, 0 );
  [super printPSCode: self];
  free( print_image );
  [self unlockFocus];
  return self;
}

- drawSelf:(const NXRect *)f :(int)rectCount;
{
  int r, c;
  NXRect cr;
  
  if( NXDrawingStatus == NX_DRAWING ){
    PSgsave();
    cr.size.width = f->size.width / 8;
    cr.size.height = f->size.height / 8;
    for( r = 0; r < 8; r++ ){
      cr.origin.y = r * cr.size.height;
      for( c = 0; c < 8; c++ ){
	cr.origin.x = c * cr.size.width;
	[square[r][c] drawSelf: &cr inView: self];
      }
    }
  
    PSsetgray( NX_BLACK );
    PSsetlinewidth( 2.0 );
    PSclippath();
    PSstroke();
    PSgrestore();
  }
  else{
    NXImageBitmap( &bounds, print_pwide, print_phigh, print_bps, print_ssp,
                   print_config, print_mask, print_image, 0, 0, 0, 0 );
  }
}

- mouseDown: (NXEvent *)event
{
  NXPoint p, pickedP, backP;
  NXHandler handler;
  NXRect pieceRect, backR;
  int r, c, r2, c2, mask, controlGState, ok;
  char const *icon;
  char *move, *pt;
  unsigned short mv;
  
  
  if( [NXApp finished] ){
    [NXApp finishedAlert];
    handler.code = 0;
    [window addToEventMask: NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
    NX_DURING
      while (event->type != NX_MOUSEUP){
	event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
      }
    NX_HANDLER
      handler = NXLocalHandler;
    NX_ENDHANDLER
    return self;
  }
  
  pickedP = event->location;
  [self convertPoint:&pickedP fromView:nil];
  backP = pickedP;
  r = (int) floor( pickedP.y / (frame.size.height / 8.0 )); 
  c = (int) floor( pickedP.x / (frame.size.width / 8.0 ));
  icon = [square[r][c] icon];
  backR.size.width = pieceRect.size.width = floor( frame.size.width / 8.0 );
  backR.size.height = pieceRect.size.height = floor( frame.size.height / 8.0 );
  backR.origin.x = backR.origin.y = 0;
  pickedP.x = pickedP.x - (((float)c) * pieceRect.size.width);
  pickedP.y = pickedP.y - (((float)r) * pieceRect.size.height);
  if( icon && [self isEnabled] ){
    controlGState = [self gState];
    if( !controlGState ){
      [self allocateGState];
      controlGState = [self gState];
    }
    [self lockFocus];
    
    PSgsave();
    /* Draw over the piece we are moving. */
    pieceRect.origin.x = c * pieceRect.size.width;
    pieceRect.origin.y = r * pieceRect.size.height;
    [square[r][c] drawBackground: &pieceRect inView: self];
    
    /* Save background */ 
    [backBitmap lockFocus];
    PSgsave();
    PScomposite( backP.x, backP.y, backR.size.width, backR.size.height,
		 controlGState, 0.0, backR.size.height, NX_COPY);
    PSgrestore();
    [backBitmap unlockFocus];
  } 
    
  handler.code = 0;
  [window addToEventMask: NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  NX_DURING
    while (event->type != NX_MOUSEUP){
      event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
      p = event->location;
      [self convertPoint:&p fromView:nil];
      if( icon && [self isEnabled] ){
        /* Restore old background */
	[self lockFocus];
	[backBitmap composite:NX_COPY toPoint: &backP];
	[self unlockFocus];
	
	/* Save new background */
	backP.x = pieceRect.origin.x = p.x - pickedP.x;
	backP.y = pieceRect.origin.y = p.y - pickedP.y;
	[backBitmap lockFocus];
	PSgsave();
	PScomposite( backP.x, backP.y,
		     backR.size.width, backR.size.height,
		     controlGState, 0.0, backR.size.height, NX_COPY);
	PSgrestore();
	[backBitmap unlockFocus];
	
	/* Draw piece at new location. */
	[square[r][c] drawInside: &pieceRect inView: self];

	PSsetgray( NX_BLACK );
	PSsetlinewidth( 2.0 );
	PSclippath();
	PSstroke();

	PSflushgraphics();
	NXPing();
      }
    }
  NX_HANDLER
    handler = NXLocalHandler;
  NX_ENDHANDLER
  
  if( icon && [self isEnabled] ){
    r2 = (int) floor( p.y / (frame.size.height / 8.0 )); 
    c2 = (int) floor( p.x / (frame.size.width / 8.0 ));
    if( r2 != r || c2 != c ){
      if( ![NXApp makeMoveFrom: r : c to: r2 : c2] ){
        [self display];
	NXPing();
      }
    }
    PSgrestore();
    [self unlockFocus];
  }

  if (handler.code) {
    NX_RAISE(handler.code, handler.data1, handler.data2);
  }
}

@end
